using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using RebEngine.Interfaces;
using RebEngine.Managers;
using Microsoft.Xna.Framework.Graphics;
using RebellionII.GameObjects;
using RebEngine.SceneObject.SceneGraph;

namespace RebEngine.SceneObject
{
    public class RebSceneObject : IRebSceneObject
    {
        private BoundingBox _bbox = new BoundingBox();
        public BoundingBox BoundingBox 
        {
            get
            {
                return _bbox;
            }
            set
            {
                BoundingBox = value;
            }
        }

        private bool _readyToRender = false;
        /// <summary>
        /// Is this object ready to render?
        /// </summary>
        public bool ReadyToRender
        {
            get { return _readyToRender; }
            set {  _readyToRender = value; }
        }

        private int _reference;
        public int GetPlanetID
        {
            get { return _reference; }
        }

        public virtual Matrix World
        {
            get
            {
                return Matrix.CreateScale(_scale) * Matrix.CreateFromQuaternion(_rotation) * Matrix.CreateTranslation(_position);
            }
        }

        private Material _material;
        public Material Material
        {
            get { return _material; }
            set { _material = value; }
        }

        private string _modelName;
        public string ModelName
        {
            get { return _modelName; }
            set { _modelName = value; }
        }

        private Vector3 _modelCenter = Vector3.Zero;
        /// <summary>
        /// The absolute center of the model in 3d space.
        /// </summary>
        public Vector3 ModelCenter
        {
            get { return _modelCenter; }
            set { _modelCenter = value; }
        }

        private float _modelRadius = 0.0f;
        /// <summary>
        /// The absolute distance from center to outer edge of the model.
        /// </summary>
        public float ModelRadius
        {
            get { return _modelRadius; }
            set { _modelRadius = value; }
        }   	

        private Vector3 _position = Vector3.Zero;
        /// <summary>
        /// The position of this object in 3d space.
        /// </summary>
        public virtual Vector3 Position
        {
            get { return _position; }
            set { _position = value;}
        }

        private Vector3 _scale = Vector3.One;
        /// <summary>
        /// Scale of the object.
        /// </summary>
        public Vector3 Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }

        private Quaternion _rotation = Quaternion.Identity;
        /// <summary>
        /// Yaw, pitch and roll of the object.
        /// </summary>
        public Quaternion Rotation
        {
            get { return _rotation; }
            set { 
                    _rotation = value;
                }
        }

        public float Distance
        {
            get
            {
                if (ReadyToRender)
                    return Vector3.Distance(CameraManager.ActiveCamera.Position, World.Translation);
                else
                    return 0.0f;
            }
        }

        public string Name { get; set; }

        public RebSceneObject(string identifier, Material theMaterial, int ID)
        {
            Name = identifier;
            _material = theMaterial;
            _reference = ID;
        }

        public void UpdateBoundingBox()
        {
            MeasureModel();
            _bbox.Min = new Vector3(_position.X - _modelRadius, _position.Y - _modelRadius, _position.Z - _modelRadius);
            _bbox.Max = new Vector3(_position.X + _modelRadius, _position.Y + _modelRadius, _position.Z + _modelRadius);
        }

        /// <summary>
        /// Whenever a new model is selected, we examine it to see how big
        /// it is and where it is centered. This lets us automatically zoom
        /// the display, so we can correctly handle models of any scale.
        /// </summary>
        public void MeasureModel()
        {
            if (ReadyToRender)
            {
                IRebModel model = ModelManager.GetModel(_modelName);
                if (model != null && model.ReadyToRender)
                {
                    // Look up the absolute bone transforms for this model.
                    Matrix[] transforms = new Matrix[model.BaseModel.Bones.Count];
                    model.BaseModel.CopyAbsoluteBoneTransformsTo(transforms);

                    // Compute an (approximate) model center position by
                    // averaging the center of each mesh bounding sphere.
                    _modelCenter = Vector3.Zero;

                    foreach (ModelMesh mesh in model.BaseModel.Meshes)
                    {
                        BoundingSphere meshBounds = mesh.BoundingSphere;
                        Matrix transform = transforms[mesh.ParentBone.Index];
                        Vector3 meshCenter = Vector3.Transform(meshBounds.Center, transform);

                        _modelCenter += meshCenter;
                    }

                    _modelCenter /= model.BaseModel.Meshes.Count;

                    // Now we know the center point, we can compute the model radius
                    // by examining the radius of each mesh bounding sphere.
                    _modelRadius = 0;

                    foreach (ModelMesh mesh in model.BaseModel.Meshes)
                    {
                        BoundingSphere meshBounds = mesh.BoundingSphere;
                        Matrix transform = transforms[mesh.ParentBone.Index];
                        Vector3 meshCenter = Vector3.Transform(meshBounds.Center, transform);

                        float transformScale = transform.Forward.Length();

                        float meshRadius = (meshCenter - _modelCenter).Length() +
                                           (meshBounds.Radius * transformScale) * _scale.X;

                        

                        _modelRadius = Math.Max(_modelRadius, meshRadius);

                    }
                }
            }
        }

        public virtual void Draw(GameTime gameTime)
        {
            if (ReadyToRender)
            {                
                IRebModel model = ModelManager.GetModel(_modelName);
                if (model != null && model.ReadyToRender)
                {
                    Matrix[] transforms = new Matrix[model.BaseModel.Bones.Count];
                    model.BaseModel.CopyAbsoluteBoneTransformsTo(transforms);

                    foreach (ModelMesh mesh in model.BaseModel.Meshes)
                    {
                        // This part is where we try and draw based on what each shader will do. 
                        if (Material.Shader == "basicEffect")
                        {
                            foreach (BasicEffect effect in mesh.Effects)
                            {
                                effect.EnableDefaultLighting();
                                effect.PreferPerPixelLighting = true;
                                Color newColour = Color.White;
                                
                                switch(NetClientManager.gameUniverse.dUniverse[GetPlanetID].CurrentOwner)
                                {
                                    case TeamName.Empire:
                                        newColour = EngineManager.EmpireColor;
                                        break;
                                    case TeamName.Alliance:
                                        newColour = EngineManager.AllianceColor;
                                        break;
                                    case TeamName.Hutts:
                                        newColour = EngineManager.HuttsColor;
                                        break;
                                    case TeamName.Smugglers:
                                        newColour = EngineManager.SmugglersColor;
                                        break;
                                    case TeamName.Default:
                                        newColour = EngineManager.DefaultColor;
                                        break;
                                }
                                effect.DiffuseColor = newColour.ToVector3();
                                effect.SpecularPower = 1.0f;
                                effect.SpecularColor = newColour.ToVector3();
                                
                                effect.World = transforms[mesh.ParentBone.Index] * World;
                                effect.View = CameraManager.ActiveCamera.View;
                                effect.Projection = CameraManager.ActiveCamera.Projection;
                            }
                        }
                        if (Material.Shader == "Planet")
                        {
                            Effect effect = ShaderManager.GetShader("Planet");

                            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                            {
                                effect.Parameters["World"].SetValue(World);
                                effect.Parameters["ColorMap0"].SetValue(TextureManager.GetTexture("planet_temperate").BaseTexture as Texture2D);
                                effect.Parameters["ColorMap1"].SetValue(TextureManager.GetTexture("earthcloudmap").BaseTexture as Texture2D);

                                foreach (ModelMeshPart part in mesh.MeshParts)
                                {
                                    part.Effect = effect;
                                    effect.Parameters["World"].SetValue(effect.Parameters["World"].GetValueMatrix()
                                        * mesh.ParentBone.Transform);
                                }
                                pass.Apply();
                            }
                        }
                        mesh.Draw();
                    }
                }
            }
        }

        public override string ToString()
        {
            return "SceneObject";
        }
    }   
}
